-- Base
-- Create by weism
-- 自动更新的一些接口封装

-- 下载资源保存的路径
local storagePaths = "update_res/";
local versionManifests  = "manifest/version.manifest";

-- 检查更新前的公告信息
function auDownloadBoard(cb)
    if type(OUT_BOARD_URL) ~= "string" or OUT_BOARD_URL == "" then
        --未配置公告获取地址，无法读取
        cb(false);
        return false;
    end

    local lang = UserDefaultM.getStringForKey("APP_LANG");
    local url  = OUT_BOARD_URL;
    local function whenResponse(responseData)
        if not responseData then
            -- 连接不上主机，默认执行自动更新
            printR("连接不上outBoard所在的主机: " .. url);
            cb(false);
            return false;
        end

        -- 替换\\n
        string.gsub(responseData, "\n", "\\n");

        -- 解析json数据
        local function decode()
            return json.decode(responseData, 1);
        end
        local status, output = xpcall(decode, function(msg) return msg; end);
        if not status then
            printR("### 解析outBoard数据失败了！");
            cb(false);
            return false;
        end

        if output["need_show"] ~= 1 then
            -- 配置不显示公告
            cb(false);
            return false;
        end

        -- 由于有语言包会在额外目录下/保证字体都有
        -- 显示完以后要去掉/在clearMyRequire()里执行去除
        cc.FileUtils:getInstance():addSearchPath(cc.FileUtils:getInstance():getWritablePath() .. storagePaths .. "res");

        -- 下载完成，通知回调
        cb(output);
    end

    -- 到远程主机获取数据/需要超时检查
    getRemoteHostData(url, whenResponse, true);
    return true;
end

-- 获取dislist配置
function auLoadDislist(cb, loadDislist2)
    local hasValidUrl = false;
    if type(DISLIST_URL_ARR) == "table" then
        for _, url in ipairs(DISLIST_URL_ARR) do
            if type(url) == "string" and string.len(url) > 0 then
                hasValidUrl = true;
                break;
            end
        end
    end
    -- 一个有效的dislist_url都没有，直接走后续流程
    if not hasValidUrl then
        -- 未配置dislist
        printR("没有有效的dislist_url地址，直接走后续流程");
        cb(false);
        return false;
    end

    local failCount = 0;
    local getValidResult = false;

    -- 同时向有效的url发出请求，采用最先响应的结果，其他的响应结果忽略
    for _, url in ipairs(DISLIST_URL_ARR) do
        local function whenResponse(responseData)
            if getValidResult then
                -- 已经获取到有效数据，其他响应忽略
                print("已经获取到有效数据，其他响应忽略, url:"..url);
                return;
            end

            if not responseData then
                failCount = failCount + 1;
                if failCount == #DISLIST_URL_ARR then
                    -- 连接不上主机，默认执行自动更新
                    printR("连接不上dislist所在的主机: " .. url);
                    cb(false);
                end
                return;
            end

            -- 解析json数据
            local function decode()
                return json.decode(responseData, 1);
            end
            local status, output = xpcall(decode, function(msg) return msg; end);
            if not status then
                printR("解析dislist数据失败！url:"..url);
                failCount = failCount + 1;

                if failCount == #DISLIST_URL_ARR then

                    cb(false);
                end
                return;
            end

            if (type(output) ~= "table") then
                printR("distlist文件格式有误。");

                failCount = failCount + 1;
                return;
            end

            getValidResult = true;
            printG("distlist数据获取成功，url:"..url);

            -- 如果配置了dislist测试标签，则使用测试标签对应的配置，否则使用默认配置
            local content;
            if type(DISLIST_TEST_TAG) == "string" and type(output[DISLIST_TEST_TAG]) == "table" then
                content = output[DISLIST_TEST_TAG];
            else
                content = output["default"];
            end
            assert(type(content) == "table", "distlist内容配置有误。");

            -- 获取完成
            cb(content);
       end

        -- 到远程主机获取数据
        if type(url) == "string" and string.len(url) > 0 then
            local targetURL = url;
            if loadDislist2 then
                targetURL = string.gsub(url, "dislist.json", "dislist2.json")
            end

            targetURL = fixUrl(targetURL);

            print("开始请求dislist数据，url:"..targetURL);
            getRemoteHostData(targetURL, whenResponse);
        else
            whenResponse(nil);
        end
    end
    return true;
end

-- 读取本地版本号
function readVersionManifest()
    local storagePaths = "update_res/";
    local targets = "assets|plus";
    local sceneManifests    = "manifest/project.manifest";
    local am = cc.AssetsManagerEx:create(sceneManifests, cc.FileUtils:getInstance():getWritablePath() .. storagePaths, targets);
    local manifest = am:getLocalManifest();
    return manifest:getVersion();
end

-- 版本号比对结果
local COMPARE_RESULT_VERSION_NOT_CHANGE   = 1;   -- 版本号不变
local COMPARE_RESULT_MAIN_VERSION_IMPROVE = 2;   -- 大版本号提升
local COMPARE_RESULT_SUB_VERSION_IMPROVE  = 3;   -- 小版本号提升

-- 对比版本号
-- 版本比对的原则是：按照ASCII码的先后顺序逐个进行比对
-- @param disVersion   dislist中的版本号
-- @param cfgVersion   version.manifest中的版本号
local compareVersion = function(disVersion, cfgVersion)
    printG(string.format("比对版本号：dislist版本号 = %s, 本地版本号 = %s", disVersion, cfgVersion));

    if cfgVersion == disVersion then
        -- 版本号不变
        return COMPARE_RESULT_VERSION_NOT_CHANGE;
    end

    if disVersion == "" or cfgVersion == "" then
        return COMPARE_RESULT_VERSION_NOT_CHANGE;
    end

    local firstDisPart = string.explode(disVersion, "-")[1];
    local firstCfgVersion = string.explode(cfgVersion, "-")[1];

    -- 分割字符串，注意：这里的点号需要转义
    local arrDis = string.explode(firstDisPart, "%.");
    local arrCfg = string.explode(firstCfgVersion, "%.");

    -- 前两段是大版本号，后两段是小版本号
    local disMainVer1 = tonumber(arrDis[1]);
    local cfgMainVer1 = tonumber(arrCfg[1]);
    local disMainVer2 = tonumber(arrDis[2]);
    local cfgMainVer2 = tonumber(arrCfg[2]);
    local disSubVer1  = tonumber(arrDis[3]);
    local cfgSubVer1  = tonumber(arrCfg[3]);
    local disSubVer2  = tonumber(arrDis[4]);
    local cfgSubVer2  = tonumber(arrCfg[4]);

    if disMainVer1 > cfgMainVer1 or
      (disMainVer1 == cfgMainVer1 and disMainVer2 > cfgMainVer2) then
        -- 大版本号提升
        return COMPARE_RESULT_MAIN_VERSION_IMPROVE;
    end

    if (disMainVer1 == cfgMainVer1 and disMainVer2 == cfgMainVer2) then
        if disSubVer1 > cfgSubVer1 or
          (disSubVer1 == cfgSubVer1 and disSubVer2 > cfgSubVer2) then
            -- 小版本号提升
            return COMPARE_RESULT_SUB_VERSION_IMPROVE;
        end
    end

    -- 显示版本号对比
    local secondDisVersion = string.explode(disVersion, "-")[2];
    local secondCfgVersion = string.explode(cfgVersion, "-")[2];
    print ("显示版本号：disversion:"..secondDisVersion..", cfgVersion:"..secondCfgVersion)
    if secondDisVersion ~= nil and secondCfgVersion ~= nil then
        arrDis = string.explode(secondDisVersion, "%.");
        arrCfg = string.explode(secondCfgVersion, "%.");

        for i = 1, 3 do
            local disVer = tonumber(arrDis[i]) or 0;
            local cfgVer = tonumber(arrCfg[i]) or 0;

            if disVer > cfgVer then
                -- 小版本号提升
                return COMPARE_RESULT_SUB_VERSION_IMPROVE;
            elseif disVer < cfgVer then
                return COMPARE_RESULT_VERSION_NOT_CHANGE;
            end
        end
    end

    -- 其他情况视为版本号不变
    return COMPARE_RESULT_VERSION_NOT_CHANGE;
end

-- 检查版本号
function auCheckVersion()
    -- 如果在维护中， 不做任何更新，直接跳至登陆界面
    -- 在登录时，会提示玩家正在维护中
    local dis = _G["dislist"];
    local clientMaintaining = dis["client_maintaining"] or "";
    if clientMaintaining == 1 and not isInWhiteList() then
        -- 维护中
        printG("版本对比结果: 维护中");
        return "maintaining";
    end

    -- 比较版本号
    local disVersion = dis["version"] or "";
    local cfgVersion = readVersionManifest();
    local ret = compareVersion(disVersion, cfgVersion);
    if ret == COMPARE_RESULT_MAIN_VERSION_IMPROVE then
        printG("版本对比结果: 大版本号变了")
        return false;
    end

    -- 如果处于灰度升级中，需要进入自动更新流程，视情况下载
    if _G["dislist"]["upgrade_by_server_version"] == 1 then
        printG("版本对比结果: 处于服务器灰度更新中，需要进入自动更新流程，视情况下载");
        return true;
    end

    -- 如果语言包未初始化，需要进入自动更新流程
    if cc.UserDefault:getInstance():getStringForKey("need_download_lang_pkg") == "1" then
        printG("版本对比结果: 语言包未下载，需要进入自动更新流程");
        return true;
    end

    local lang = UserDefaultM.getStringForKey("APP_LANG");
    if lang == nil or lang == "" then
        printG("版本对比结果: 语言包未初始化，需要进入自动更新流程");
        return true;
    end

    -- 获取本地版本后，检查lua搜索路径是否已经包含update_res，如果不包含，说明SD卡内容未生效，需要进入自动更新流程
    local isUpdateResValid = false;
    local pathList = cc.FileUtils:getInstance():getSearchPaths();
    for _, p in ipairs(pathList) do
        if string.find(p, "update_res") then
            isUpdateResValid = true;
        end
    end

    if not isUpdateResValid then
        printG("版本对比结果: SD卡内容未生效，需要进入自动更新流程");
        return true;
    end

    if ret == COMPARE_RESULT_VERSION_NOT_CHANGE then
        printG("版本对比结果: 版本号不变")
        return "=";
    end

    -- 不需要更新
    printG("版本对比结果:小版本号提升")
    return true;
end

-- 获取apk下载链接
function getPackageDownloadUrl()
    if not _G["dislist"] or not _G["dislist"]["package_url"] then
        -- 没有配置下载链接
        return "";
    end

    if isAndroidPlatform() then
        return _G["dislist"]["package_url"]["android"] or "";
    else
        return _G["dislist"]["package_url"]["ios"] or "";
    end
end

-- 测试网络连通性
function auTestNetwork(node, callback)
    local urlList = {
        "https://update.leiting.com/gumballs/leiting/ios/dislist/check_network.php",
        "https://www.baidu.com/duty/copyright.html",
        "https://php.weather.sina.com.cn/iframe/index/w_cl.php?code=js&day=0&city=&dfc=1&charset=utf-8"
    };

    local ret = {};
    for _, url in ipairs(urlList) do
        ret[url] = {};
        local function whenResponse(responseData)
            if not responseData then
                ret[url]["result"] = 0;
                return;
            end

            ret[url]["result"] = 1;
        end
        getRemoteHostData(url, whenResponse);
    end

    local function checkFinish()
        for _, url in ipairs(urlList) do
            if ret[url]["result"] == nil then
                performWithDelay(node, checkFinish, 1);
                return;
            end
        end

        local success = false;
        for _, url in ipairs(urlList) do
            if ret[url]["result"] == 1 then
                success = true;
                break;
            end
        end

        -- 都测试完成
        callback(success);
    end
    performWithDelay(node, checkFinish, 1);
end
